package com.yanghui.dtracing.boot;

import com.yanghui.dtracing.boot.banner.BannerPrint;
import com.yanghui.dtracing.core.PluginBootstrap;
import com.yanghui.dtracing.core.PluginInstrumentBoot;
import com.yanghui.dtracing.core.PluginSearch;
import com.yanghui.dtracing.core.config.AgentConfig;
import com.yanghui.dtracing.core.config.ConfigParser;
import com.yanghui.dtracing.core.log.ILog;
import com.yanghui.dtracing.core.log.LogManager;
import com.yanghui.dtracing.core.opentracing.Tracing;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;

import java.lang.instrument.Instrumentation;
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;

public class AgentBootApplication {
	
	private static ILog log;
	
	static {
		LogManager.init();
	}

    public static void premain(String agentArgs, Instrumentation inst){
	    System.setProperty("agent.version",Version.getVersion());
    	BannerPrint.printBanner();
    	AgentConfig config = ConfigParser.parseAgentArgs(agentArgs);
    	LogManager.setLogContext(config);
    	log = LogManager.getLogger(AgentBootApplication.class);
        Tracing.init(config);
        Runtime.getRuntime().addShutdownHook(new Thread(()-> Tracing.getTracer().close()));
        final PluginSearch pluginSearch;
        try{
            PluginBootstrap pluginBootstrap = new PluginBootstrap(config);
            pluginSearch = new PluginSearch(pluginBootstrap.loadPlugins());
            if(!pluginSearch.isHashPlugin()){
                log.info("======================No plug-ins have been loaded==================");
                return;
            }
            pluginSearch.printPlugin();
        }catch (Throwable t){
            log.error("An exception has occurred in plug-in loading：", t);
            return;
        }

        AgentBuilder agentBuilder = new AgentBuilder.Default()
                .ignore(nameStartsWith("net.bytebuddy.")
                        .or(nameStartsWith("org.slf4j."))
                        .or(nameStartsWith("org.groovy."))
                        .or(nameStartsWith("javassist"))
                        .or(nameContains(".asm."))
                        .or(nameContains(".reflectasm."))
                        .or(nameStartsWith("sun.reflect."))
                        .or(nameStartsWith("com.yanghui.dtracing."))
                        .or(ElementMatchers.isSynthetic())
                );

        try{
            agentBuilder = PluginInstrumentBoot.enhance(pluginSearch,agentBuilder);
        }catch (Throwable t){
            log.error("Class enhancement has an exception：", t);
            return;
        }
        agentBuilder.with(new AgentListener()).installOn(inst);
        log.info("Agent loading is completed");
    }

    public static class AgentListener implements AgentBuilder.Listener{

        @Override
        public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }

        @Override
        public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
        }

        @Override
        public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }

        @Override
        public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
            System.err.println("========================Agent has an exception=======================" + typeName);
            throwable.printStackTrace();
        }

        @Override
        public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
        }
    }
}
